﻿using PLCSharp.CoreModel.Common;
using PLCSharp.Models;
using Prism.Commands;
using Prism.Mvvm;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using System.Reflection;
using System.Threading;

namespace PLCSharp.Views.Workflows
{
    public class Workflow : BindableBase
    {
        [Key]
        public Guid ID { get; set; } = Guid.NewGuid();
        public Guid RecipeID { get; set; }

        private string _Name;
        /// <summary>
        /// Comment
        /// </summary>
        public string Name
        {
            get { return _Name; }
            set
            {
                if (_Name != value)
                {
                    Prompt = "已修改，请保存";
                }
                SetProperty(ref _Name, value);
            }
        }

        private string _Prompt;
        [NotMapped]
        /// <summary>
        /// 提示
        /// </summary>
        public string Prompt
        {
            get { return _Prompt; }
            set { SetProperty(ref _Prompt, value); }
        }
        private double _CycleDelayTime = 0.01;
        /// <summary>
        /// 循环结束延迟
        /// </summary>
        public double CycleDelayTime
        {
            get { return _CycleDelayTime; }
            set { SetProperty(ref _CycleDelayTime, value); }
        }
        private string _Code;



        private double _CycleTime;
        [NotMapped]
        /// <summary>
        /// 
        /// </summary>
        public double CycleTime
        {
            get { return _CycleTime; }
            set { SetProperty(ref _CycleTime, value); }
        }

        /// <summary>
        /// Comment
        /// </summary>
        public string Code
        {
            get { return _Code; }
            set
            {
                if (_Code != value)
                {
                    IsCompiled = false;
                    Prompt = "已修改，请编译";
                }
                SetProperty(ref _Code, value);
            }
        }
        private string _Comment;
        /// <summary>
        /// 备注
        /// </summary>
        public string Comment

        {
            get { return _Comment; }
            set
            {
                if (_Comment != value)
                {
                    Prompt = "已修改，请保存";
                }
                SetProperty(ref _Comment, value);
            }
        }

        private bool _AutomaticExecution;
        /// <summary>
        /// 脚本自动运行
        /// </summary>
        public bool AutomaticExecution
        {
            get { return _AutomaticExecution; }
            set { SetProperty(ref _AutomaticExecution, value); }
        }

        private bool _IsCompiled;
        [NotMapped]
        /// <summary>
        /// 已编译
        /// </summary>
        public bool IsCompiled
        {
            get { return _IsCompiled; }
            set { SetProperty(ref _IsCompiled, value); }
        }

        private bool _IsRuning;
        [NotMapped]
        /// <summary>
        /// 
        /// </summary>
        public bool IsRuning
        {
            get { return _IsRuning; }
            set { SetProperty(ref _IsRuning, value); }
        }

        [NotMapped]
        public Assembly Assembly { get; set; }

        private string _DebugLog;
        [NotMapped]
        /// <summary>
        /// 调试日志
        /// </summary>
        public string DebugLog
        {
            get { return _DebugLog; }
            set { SetProperty(ref _DebugLog, value); }
        }


        private bool _Exception;
        [NotMapped]
        /// <summary>
        /// 异常
        /// </summary>
        public bool Exception
        {
            get { return _Exception; }
            set { SetProperty(ref _Exception, value); }
        }

        AssemblyCSharpBuilder AssemblyCSharpBuilder;


        private DelegateCommand _Compile;
        public DelegateCommand Compile =>
            _Compile ??= new DelegateCommand(ExecuteCompile);

        void ExecuteCompile()
        {

            lock (this)
            {
                try
                {
                    Flow.Reset();
                    AssemblyCSharpBuilder = new();
                    DebugLog = "编译中，请稍等。\n";
                    AssemblyCSharpBuilder.ConfigLoadContext(ctx => ctx.AddReferenceAndUsingCode<object>());

                    AssemblyCSharpBuilder.Add(Code);
                    AssemblyCSharpBuilder.LogCompilationEvent += (log) => { DebugLog += log.ToString(); };

                    Assembly = AssemblyCSharpBuilder.GetAssembly();
                    DebugLog += "编译完成。\n";
                    Prompt = "";
                    IsCompiled = true;
                    Exception = false;
                    action = Assembly.GetDelegateFromShortName<Action<GlobalModel,FlowModel>>("Extension_" + ID.ToString().Replace("-", ""), "Run");
                }
                catch (Exception e)
                {
                    DebugLog += e.Message;

                }


            }
        }

        private Action<GlobalModel,FlowModel> action;
        public void Run(GlobalModel globalModel)
        {
            if (Assembly == null || !IsCompiled || Exception)
            {
                return;
            }
            try
            {
                action?.Invoke(globalModel,Flow);
            }
            catch (Exception ex)
            {
                DebugLog += $"运行时异常: {ex}\n";
                Exception = true;
            }

        }

        private BackgroundWorker _BackgroundWorker;
        public void Start(GlobalModel globalModel)
        {
            GlobalModel = globalModel;
            _BackgroundWorker = new BackgroundWorker
            {
                WorkerSupportsCancellation = true
            };
            _BackgroundWorker.DoWork += BackgroundWork;
            _BackgroundWorker.RunWorkerAsync();
        }
        public void Stop()
        {
            if (_BackgroundWorker != null && _BackgroundWorker.IsBusy)
            {
                _BackgroundWorker.CancelAsync();
                _BackgroundWorker.Dispose();
            }
            IsRuning = false;
        }
        private void BackgroundWork(object sender, DoWorkEventArgs e)
        {
            var worker = (BackgroundWorker)sender;
            var sw = Stopwatch.StartNew();
            while (!worker.CancellationPending)
            {

                sw.Restart();
                Thread.Sleep((int)(CycleDelayTime * 1000));
                if (AutomaticExecution)
                {
                    IsRuning = true;
                    Run(GlobalModel);
                }
                else
                {
                    IsRuning = false;
                }
                sw.Stop();
                CycleTime = sw.Elapsed.TotalSeconds;
            }
        }
        GlobalModel GlobalModel;
        private FlowModel _Flow = new();
        [NotMapped]
        /// <summary>
        /// 
        /// </summary>
        public FlowModel Flow
        {
            get { return _Flow; }
            set { SetProperty(ref _Flow, value); }
        }


    }



}
